#pragma once
#include "Protocol.h"
#include "Player.h"
#include "Board.h"
#include "BuildPoint.h"
#include "Edge.h"
#include "House.h"
#include "ResourceCard.h"
#include "Road.h"
#include "DevelopmentCard.h"
#include "Field.h"
#include "FieldDieNum.h"
#include "Robber.h"
#include "BoardItems.h"
#include "DieNumber.h"

namespace catan
{
	class DieRandomer;
	class BoardLogic
	{
	public:
		BoardLogic(void);
		virtual ~BoardLogic(void);

		bool CheckStrategyColor(PlayerColor::Enum color)
		{
			if (GetStrategyColor()== color)
			{
				return true;
			}
			LogError(_T("Not Same StrategyColor Hack or some bug"));
			return false;
		}

		bool CanDie()
		{
			return !mBoard->mDied;
		}

		PlayerColor::Enum GetStrategyColor()
		{
			return mBoard->mPlayers[mBoard->mCurrPlayerNum]->mColor;
		}


		Handle<Player> GetPlayer( PlayerColor::Enum color )
		{
			for(Players::iterator it = mBoard->mPlayers.begin(); 
				it != mBoard->mPlayers.end(); ++it)
			{
				if ((*it)->mColor == color)
				{
					return *it;
				}
			}
			return Handle<Player>();
		}

		bool CanBuildPoint(int buildPointId)
		{
			if (mBoard->mBuildPoints[buildPointId]->mBuilding)
			{
				LogDebug(_T("BuildPoint is Full"));
				return false;
			}
			return true;
		}

		BuildPoints FindAllBuildPoints()
		{
			BuildPoints ret;
			for (BuildPoints::iterator it(mBoard->mBuildPoints.begin());
			it != mBoard->mBuildPoints.end(); ++it)
			{
				if (!(*it)->HasBuilding() && !IsNearBuilding(*it))
				{
					ret.push_back(*it);
				}
			}
			return ret;
		}

		BuildPoints FindBuildHousePoints(PlayerColor::Enum color)
		{
			BuildPoints ret;
			if (IsPreBuilding())
			{
				return FindAllBuildPoints();
			}
			FOR_EACH (BuildPoints, it, mBoard->mBuildPoints)
			{
				if ((*it)->mBuilding)
				{
					continue;
				}

				if (!IsNearRoad(color, *it))
				{
					continue;
				}

				if (!IsNearBuilding(*it))
				{
					ret.push_back(*it);
				}
			}
			return ret;
		}

		BuildPoints FindBuildSettlementPoints(PlayerColor::Enum color)
		{
			BuildPoints ret;
			if (IsPreBuilding())
			{
				return FindAllBuildPoints();
			}
			FOR_EACH (BuildPoints, it, mBoard->mBuildPoints)
			{
				Handle<Building>  building = (*it)->mBuilding;
				if (building && building->GetColor() == color)
				{
					VALID(!IsNearBuilding(*it));
					ret.push_back(*it);
				}
			}
			return ret;
		}

		Edges FindAllBuildEdges(PlayerColor::Enum color)
		{
			Edges ret;

			Handle<Player> p = GetPlayer(color);
			VALID(p);
			std::set<Handle<Edge>> edgeSet;
			FOR_EACH_CONST(std::vector<Handle<House>>, houseIt, p->mPlayerPack->mHouses.GetVector())
			{
				if((*houseIt)->HasBuilt())
				{
					FOR_EACH_CONST(Edges, edgeIt, (*houseIt)->GetBuildPoint()->mEdges)
					{
						if (!(*edgeIt)->mRoad)
						{
							edgeSet.insert(*edgeIt);
						}
					}
				}
			}

			FOR_EACH_CONST(std::vector<Handle<Settlement>>, houseIt, p->mPlayerPack->mSettlements.GetVector())
			{
				if((*houseIt)->HasBuilt())
				{
					FOR_EACH_CONST(Edges, edgeIt, (*houseIt)->GetBuildPoint()->mEdges)
					{
						if (!(*edgeIt)->mRoad)
						{
							edgeSet.insert(*edgeIt);
						}
					}
				}
			}

			FOR_EACH_CONST(std::vector<Handle<Road>>, roadIt, p->mPlayerPack->mRoads.GetVector())
			{
				if((*roadIt)->HasBuilt())
				{
					FOR_EACH_CONST(BuildPoints, buildPointIt, (*roadIt)->GetEdge()->mBuildPoints)
					{
						FOR_EACH_CONST(Edges, edgeIt, (*buildPointIt)->mEdges)
						{
							if ((*edgeIt) == (*roadIt)->GetEdge())
							{
								continue;
							}
							if (!(*edgeIt)->mRoad)
							{
								edgeSet.insert(*edgeIt);
							}

						}
					}
				}
			}
			std::copy(edgeSet.begin(), edgeSet.end(), std::back_inserter(ret));
			return ret;
		}

		bool IsNearBuilding(Handle<BuildPoint> buildPoint)
		{
			FOR_EACH (Edges, it, buildPoint->mEdges)
			{
				if ((*it)->GetOtherBuildPoint(buildPoint)->mBuilding)
				{
					return true;
				}
			}
			return false;
		}

		bool IsNearBuilding(PlayerColor::Enum color, Handle<Edge> edge)
		{
			return (edge->mBuildPoints[0]->mBuilding && edge->mBuildPoints[0]->mBuilding->GetColor() == color)
				|| (edge->mBuildPoints[1]->mBuilding && edge->mBuildPoints[1]->mBuilding->GetColor() == color);
		}

		bool IsNearRoad(PlayerColor::Enum color, Handle<BuildPoint> buildPoint, Handle<Edge> exceptEdge = Handle<Edge>())
		{
			for(Edges::iterator edIt(buildPoint->mEdges.begin());
				edIt != buildPoint->mEdges.end(); ++edIt)
			{
				if ((*edIt) != exceptEdge && (*edIt)->mRoad && (*edIt)->mRoad->GetColor() == color)
				{
					return true;
				}
			}
			return false;
		}

		bool IsNearRoad(PlayerColor::Enum color, Handle<Edge> edge)
		{
			for (BuildPoints::iterator it(edge->mBuildPoints.begin());
				it != edge->mBuildPoints.end(); ++it)
			{
				if (IsNearRoad(color, *it, edge))
				{
					return true;
				}
				
			}
			return false;
		}

		bool CanBuildHouse(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			
			Handle<BuildPoint> buildPoint(id, mBoard->mBoardItems->Get<BuildPoint>(id));
			if (!buildPoint)
			{
				LogError(_T("no buildPoint") + FMTVAR(id));
				return false;
			}
			if (!p)
			{
				LogError(_T("no player"));
				return false;
			}
			if (!IsPreBuilding() && !CanBuy<House>(p))
			{
				LogDebug(_T("Can not buy"));
				return false;
			}

			if (p->mPlayerPack->mHouses.Empty())
			{
				LogDebug(_T("House is empty"));
				return false;
			}
		
			if (!IsPreBuilding() && !IsNearRoad(color, buildPoint))
			{
				LogDebug(_T("there are no road near"));
				return false;
			}

			if (IsNearBuilding(buildPoint))
			{
				LogDebug(_T("has building near"))
				return false;
			}

			if (buildPoint->mBuilding)
			{
				LogDebug(_T("BuildPoint is Full"));
				return false;
			}
			return true;
		}

		bool CanBuildSettlement(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			Handle<BuildPoint> buildPoint(id, mBoard->mBoardItems->Get<BuildPoint>(id));
			if (!buildPoint)
			{
				LogError(_T("no buildPoint") + FMTVAR(id));
				return false;
			}
			if (!p)
			{
				LogError(_T("no player"));
				return false;
			}

			if (!IsPreBuilding() && !CanBuy<Settlement>(p))
			{
				LogDebug(_T("Can not buy"));
				return false;
			}

			VALID (IsNearRoad(color, buildPoint));
// 			{
// 				LogDebug("there are no road near");
// 				return false;
// 			}

			if (p->mPlayerPack->mSettlements.Empty())
			{
				LogDebug(_T("Settlement is empty"));
				return false;
			}
			if (!buildPoint->mBuilding)
			{
				LogDebug(_T("BuildPoint is Not Build"));
				return false;
			}
			if ((buildPoint->mBuilding.DynamicCast<House>()))
			{
				LogDebug(_T("BuildPoint is Built"));
				return false;
			}
			return true;
		}

		bool CanBuildRoad(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			Handle<Edge> edge(id, mBoard->mBoardItems->Get<Edge>(id));
			if (!edge)
			{
				LogError(_T("no edge") + FMTVAR(id));
				return false;
			}
			if (!p)
			{
				LogError(_T("no player"));
				return false;
			}

			if (!IsPreBuilding() && !CanBuy<Road>(p))
			{
				LogDebug(_T("Can not buy"));
				return false;
			}

			if (p->mPlayerPack->mRoads.Empty())
			{
				LogDebug(_T("Road is empty"));
				return false;
			}
			if (!IsPreBuilding() && !IsNearRoad(color, edge))
			{
				LogDebug(_T("there are no road near"));
				return false;
			}
			if (IsPreBuilding() && !IsNearBuilding(color, edge))
			{
				LogDebug(_T("there are no road building"));
				return false;
			}
			
			if (edge->mRoad)
			{
				LogDebug(_T("Edge is Empty"));
				return false;
			}
			return true;
		}

		void BuildHouse(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			Handle<BuildPoint> buildPoint = mBoard->GetHandle<BuildPoint>(id);
			buildPoint->mBuilding = p->mPlayerPack->mHouses.Use();
			buildPoint->mBuilding->SetBuildPoint(buildPoint);
			CheckPayFor<House>(p);
		}

		void BuildSettlement(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			Handle<BuildPoint> buildPoint = mBoard->GetHandle<BuildPoint>(id);
			buildPoint->mBuilding->SetBuildPoint(Handle<BuildPoint>());
			p->mPlayerPack->mHouses.Recycle(buildPoint->mBuilding.DynamicCast<House>());

			buildPoint->mBuilding = p->mPlayerPack->mSettlements.Use();
			buildPoint->mBuilding->SetBuildPoint(buildPoint);
			CheckPayFor<Settlement>(p);

		}

		void BuildRoad(PlayerColor::Enum color, int id)
		{
			Handle<Player> p = GetPlayer(color);
			Handle<Edge> edge = mBoard->GetHandle<Edge>(id);
			edge->mRoad = p->mPlayerPack->mRoads.Use();
			edge->mRoad->SetEdge(edge);
			CheckPayFor<Road>(p);
		}

		bool IsPreBuilding()
		{
			return mBoard->IsPrebuilding();
		}

		bool IsPreBuildingFirst()
		{
			return mBoard->IsPrebuildingFirst();
		}

		bool CanBuyDevelopmentCard(PlayerColor::Enum color)
		{
			Handle<Player> p = GetPlayer(color);
			if (p)
			{
				LogError(_T("no player"));
				return false;
			}
			if (mBoard->mDevelopmentCards.empty())
			{
				LogDebug(_T("not DevelopmentCard"));
				return false;
			}
			if (!CanBuy<DevelopmentCard>(p))
			{
				LogDebug(_T("can not buy DevelopmentCard"));
				return false;
			}
			return true;
		}

		Handle<DevelopmentCard> BuyDevelopCard(PlayerColor::Enum color)
		{
			Handle<DevelopmentCard> card = mBoard->mDevelopmentCards.back();
			mBoard->mDevelopmentCards.pop_back();
			Handle<Player> p = GetPlayer(color);
			card->SetPlayer(p);

			CheckPayFor<DevelopmentCard>(p);
			return card;
		}

		template<class PlayerItemType>
		void CheckPayFor(Handle<Player> player)
		{
			if (!IsPreBuilding())
			{
				PayFor(player, PlayerItemType::GetBuyCards());
			}
		};

		void PayFor(Handle<Player> player, FieldResourceTypes& resourceTypes)
		{
			struct ResourceCardCmp
			{
				ResourceCardCmp(FieldResourceType::Enum type) : mType(type) {}
				bool operator () (Handle<ResourceCard> card) const
				{
					return  (card->Type() == mType);
				}

				FieldResourceType::Enum mType;
					
			};
			for (FieldResourceTypes::iterator it(resourceTypes.begin());
				it != resourceTypes.end(); ++it)
			{
				ResourceCards::iterator cardIt = std::find_if(player->mResourceCards.begin(), player->mResourceCards.end(), ResourceCardCmp(*it));
				if (cardIt == player->mResourceCards.end())
				{
					LogError(_T("not this card"));
					return ;
				}
				mBoard->mResourceCards[*it].push_back(*cardIt);
				player->mResourceCards.erase(cardIt);
								
			}

		}
		
		template<class PlayerItemType>
		bool CanBuy(Handle<Player> player)
		{
			return CanBuy(player, PlayerItemType::GetBuyCards());
		}

		bool CanBuy(Handle<Player> player, FieldResourceTypes& resourceTypes)
		{
			int resourceCards[FieldResourceType::Num] = {0};
			for (ResourceCards::iterator it(player->mResourceCards.begin()); it != player->mResourceCards.end(); ++it)
			{
				++resourceCards[(*it)->Type()];
			}
			for (FieldResourceTypes::iterator it(resourceTypes.begin());
				it != resourceTypes.end(); ++it)
			{
				--resourceCards[*it];
				if (resourceCards[*it] < 0)
				{
					return false;
				}
			}
			return true;
		}

		bool IsEndGame(PlayerColor::Enum color)
		{
			Handle<Player> p = GetPlayer(color);
			if (p)
			{
				LogError(_T("p is null sth wrong"));
				return false;
			}
		}
		void NextColor()
		{
			switch (mBoard->mGamePlayState)
			{
			case GamePlayState::PrebuildingFirst:
				++mBoard->mCurrPlayerNum;
				if (mBoard->mCurrPlayerNum == mBoard->mPlayers.size())
				{
					mBoard->mGamePlayState = GamePlayState::PrebuildingSecond;
					--mBoard->mCurrPlayerNum;
				}
				break;
			case GamePlayState::PrebuildingSecond:
				--mBoard->mCurrPlayerNum;
				if (mBoard->mCurrPlayerNum < 0)
				{
					mBoard->mCurrPlayerNum = 0;
					mBoard->mGamePlayState = GamePlayState::GamePlaying;
				}
				break;
			case GamePlayState::GamePlaying:
				mBoard->mCurrPlayerNum = (mBoard->mCurrPlayerNum + 1) % mBoard->mPlayers.size();
				mBoard->mDied = false;
				break;
			}
		}

		DieNumber DieNum();

		bool FindDieFiled(int totalDie, Fields& fields)
		{
			for (Fields::iterator it(mBoard->mFields.begin());
				it != mBoard->mFields.end(); ++it)
			{
				if ((*it)->mDiceNum && (*it)->mDiceNum->mDieNum == totalDie)
				{
					fields.push_back(*it);
				}
				else if (totalDie == Robber::DieNum())
				{
					fields.push_back(*it);
				}
			}
			return true;
		}

		void GetPlayerHarvestResourceCards(const Fields& fields, PlayersHarvestResourceCards& playersHarvestResourceCards)
		{
			if (fields[0]->mRobber)
			{
				LogError(_T("there are have robber"));
				return ;
			}
			int ResourceNum[FieldResourceType::Num] = {0};
			for (Fields::const_iterator it(fields.begin());
				it != fields.end(); ++it)
			{
				for(BuildPoints::const_iterator ptIt((*it)->mBuildPoints.begin());
					ptIt != (*it)->mBuildPoints.end(); ++ptIt)
				{
					Handle<Building> building = (*ptIt)->mBuilding;
					if (!building)
					{
						continue;
					}
					ResourceNum[(*it)->mFieldResource->Type()] += building->GetResourceCardHarvestNum();
				}
			}
			for (int i = 0; i < FieldResourceType::Num; ++i)
			{
				if (ResourceNum[i] > (int)mBoard->mResourceCards[i].size())
				{
					ResourceNum[i] = -1; //not enough to give everyone;
				}
			}

			for (Fields::const_iterator it(fields.begin());
				it != fields.end(); ++it)
			{
				for(BuildPoints::const_iterator ptIt((*it)->mBuildPoints.begin());
					ptIt != (*it)->mBuildPoints.end(); ++ptIt)
				{
					Handle<Building> building = (*ptIt)->mBuilding;
					if (!building)
					{
						continue;
					}
					FieldResourceType::Enum type = (*it)->mFieldResource->Type();
					if (ResourceNum[type] != -1)
					{
						for (int i = 0; i < building->GetResourceCardHarvestNum(); ++i)
						{
							assert(!mBoard->mResourceCards[type].empty());
							playersHarvestResourceCards[building->GetColor()].push_back(mBoard->mResourceCards[type].back());
							mBoard->mResourceCards[type].pop_back();
						}
					}
				}
			}
		}

		void SetPlayerHarvestResourceCards(PlayersHarvestResourceCards& playersHarvestResourceCards)
		{
			for (PlayersHarvestResourceCards::iterator it(playersHarvestResourceCards.begin());
				it != playersHarvestResourceCards.end(); ++it)
			{
				for (ResourceCards::iterator resCardIt(it->second.begin());
					resCardIt != it->second.end(); ++resCardIt)
				{
					*resCardIt = mBoard->mBoardItems->ToHandle(mBoard->mBoardItems->Get<ResourceCard>(resCardIt->Id()));
					Handle<Player> p = GetPlayer(it->first);
					VALID(p);
					p->mResourceCards.push_back(*resCardIt);
					mBoard->mResourceCards[(*resCardIt)->Type()].pop_back();
				}
			}
		}

		Handle<Board> GetBoard() const { return mBoard; }

		Handle<Board> mBoard;
		DieRandomer* mDieRandomer;
	};

	class IBoardLogicClient;
	class IBoardLogicServer : virtual public BoardLogic
	{
	public:
		IBoardLogicServer()
		{
	
		}
		virtual ~IBoardLogicServer() {}

		virtual void OnSEvent(ProtocolBase& protocol){VALID(0);};
		
// 		virtual void OnSEvent(BuildRoadReq&) = 0;
// 		virtual void OnSEvent(BuildSettlementReq&) = 0;
// 		virtual void OnSEvent(BuyCardReq&) = 0;
// 		virtual void OnSEvent(DieReq&) = 0;
// 		virtual void OnSEvent(UseCardReq&) = 0;
		//virtual IBoardLogicClient* GetBoardLogicClient(PlayerColor::Enum color = PlayerColor::Num) = 0;
	protected:
		
// 		struct Clients : std::vector<IBoardLogicClient*>
// 		{
// 			void Begin(){mIt = this->begin();}
// 			bool End() { mIt != this->end();}
// 			IBoardLogicClient* Curr() {return *(mIt++);}
// 			Clients::iterator mIt;
// 		}mClients;
	};
	
	class IBoardLogicClient : virtual public BoardLogic
	{
	public:
//  		virtual void OnCEvent(SetGameStart&) = 0;
//  		virtual void OnCEvent(BuildHouseApply&) = 0;
//  		virtual void OnCEvent(BuildSettlementApply&) = 0;
//  		virtual void OnCEvent(BuildRoadApply&) = 0;
//  		virtual void OnCEvent(BuyCardApply&) = 0;
//  		virtual void OnCEvent(EndTurnApply&) = 0;
//  		virtual void OnCEvent(DieApply&) = 0;
//  		virtual void OnCEvent(SetPlayerTrun&) = 0;
//  		virtual void OnCEvent(PlayersHarvestApply&) = 0;

		PlayerColor::Enum GetColor() const { return mColor; }
		void SetColor(PlayerColor::Enum val) { mColor = val; }
		void SetServer(IBoardLogicServer* val) { mServer = val; }
		virtual void OnCEvent(ProtocolBase& protocol){VALID(0);};

	protected:
		IBoardLogicServer* mServer;
		PlayerColor::Enum mColor;
		
	};
}	